// File: CsoGateway.Collections.List.js
// Version: 0.7.1.0
// Author: Pascal Dufresne
// Date: 2008-08-05
// Last update: 2009-05-25
// http://csogateway.codeplex.com
// http://csogateway.metaobjects.ca
// Copyright (C) 2010 Pascal Dufresne

/*
 * Register CsoGateway.Collections namespace. Import CsoGateway.System namespace.
 */
Type.registerNamespace("CsoGateway.Collections");
ImportNamespace(CsoGateway.System);

/*
 * CsoGateway.Collections.List is a collection of ordered objects
 * that can be accessed by index. It's interface is meant to resemble
 * that of the .NET class System.Collections.Generic.List<System.Object>
 * It's internal implementation is a simple javascript Array object.
 *
 * The respect of its interface is strictly enforced. All method parameters
 * must be specified, they must be of the right type and within an acceptable
 * range or an exception is thrown.
 *
 * It allows the same object to be added many times.
 * Null values can be added but not undefined values.
 *
 * This class uses the MicrosoftAjax.js Type extensions.
 * It is build on top of the MicrosoftAjax.js library and cannot function without it.
 *
 * Contructor                       'System.Collections.Generic.List<System.Object>' equivalent
 * ----------                       -----------------------------------------------------------
 * List()                           List()
 * List(<argument list>)            List(System.Collections.Generic.IEnumerable<System.Object>)
 *
 *
 * Methods                          'System.Collections.Generic.List<System.Object>' equivalent
 * ----------                       -----------------------------------------------------------
 * Count()                          Count
 * IsEmpty()                        -
 * First()                          -
 * Last()                           -
 * getItem(Number)                  Item{get;}
 * setItem(Number, Object)          Item{set;}
 * Add(Object)                      Add(System.Object)
 * AddRange(List)                   AddRange(IEnumerable<System.Object>)
 * AddArray(Array)                  -
 * Insert(Number, Object)           Insert(int, System.Object)
 * InsertRange(Number, List)        InsertRange(int, IEnumerable<System.Object>)
 * InsertArray(Number, Array)       -
 * GetRange(Number, Number)         GetRange(int, int)
 * Clear()                          Clear()
 * IndexOf(Object)                  IndexOf(System.Object)
 * LastIndexOf(Object)              LastIndexOf(System.Object)
 * Remove(Object)                   Remove(System.Object)
 * RemoveAt(Number)                 RemoveAt(int)
 * RemoveRange(Number, Number)      RemoveRange(int,int)
 * sort(function)                   sort(System.Collections.Generic.IComparer)
 * 
 *
 */


/*
 * Creates a new CsoGateway.Collections.List.
 * The internal Array is created.
 * Objects in the parameter list are added to the List in order using the setItem(Number, Object) method.
 */
CsoGateway.Collections.List = function List()
{	
	if(arguments.length == 0)
	{
		this.elements = new Array();
	}
	else
	{
		this.elements = new Array(arguments.length);
		for(var i=0; i < arguments.length; ++i)
		{
			this.setItem(i, arguments[i]);
		}
	}
}

/*
 * Gets the number of elements actually contained in the List.
 * Ref: System.Collection.Generic.List.Count 
 */
CsoGateway.Collections.List.prototype.Count =  function()
{
	return (this.elements.length);
}
	
/*
 * Tests whether this list is currently empty (has no elements).
 */
CsoGateway.Collections.List.prototype.IsEmpty =  function()
{
	return (this.elements.length == 0);
}
	

/*
 * Gets the first element in this list.
 * Throws an exception if the list is empty.
 */
CsoGateway.Collections.List.prototype.First = function()
{
	if (this.IsEmpty())
		throw new Error('CsoGateway.Collections.List.prototype.First: The List is empty');

	return this.getItem(0);
}

/*
 * Gets the last element in this list.
 * Throws an exception if the list is empty.
 */
CsoGateway.Collections.List.prototype.Last = function()
{
	if (this.IsEmpty())
		throw new Error('CsoGateway.Collections.List.prototype.Last: The List is empty');

	return this.getItem(this.elements.length - 1);
}

/*
 * Sets the element at the specified index to the element passed in parameter.
 * The element can be null but cannot be undefined or an exception is thrown.
 * If the index is not of type Number, is smaller than zero or larger than 
 * the current size of the List minus one, an exception is thrown.
 * Ref: System.Collection.Generic.List.Item
 */
CsoGateway.Collections.List.prototype.setItem = function(index, element)
{
	AssertArgumentIsNumberInRange(index, 0, (this.elements.length - 1), 'index in CsoGateway.Collections.List.prototype.setItem');
	AssertArgumentNotUndefined(element, 'element in CsoGateway.Collections.List.prototype.setItem');
	
	this.elements[index] = element;
}

/*
 * Gets the element at the specified index.
 * If the index is not of type Number, is smaller than zero or larger than 
 * the current size of the List minus one, an exception is thrown.
 * Ref: System.Collection.Generic.List.Item
 */
CsoGateway.Collections.List.prototype.getItem = function(index)
{
	AssertArgumentIsNumberInRange(index, 0, (this.elements.length - 1), 'index in CsoGateway.Collections.List.prototype.getItem');

	return this.elements[index];
}

/*
 * Adds an item to the List.
 * The element can be null but cannot be undefined or an exception is thrown.
 * The element cannot be the current List or an exception is thrown.
 * Ref: System.Collection.IList.Add
 */
CsoGateway.Collections.List.prototype.Add = function(element)
{
	AssertArgumentNotUndefined(element, 'element in CsoGateway.Collections.List.prototype.Add');
	
	if(element === this)
		throw new Error('CsoGateway.Collections.List.prototype.Add: Cannot add the List to itself.');
	
	this.elements[this.elements.length] = element;
}

/*
 * Adds the elements of the List specified to the end of the current List.
 * The list parameter cannot be undefined or null. 
 * Elements in the list can be null but cannot be undefined.
 * The elements are inserted in the same order in the List as they are
 * in the array.
 * The list parameter cannot be the current List or an exception is thrown.
 * Ref: System.Collection.Generic.List.AddRange
 */
CsoGateway.Collections.List.prototype.AddRange = function(list)
{
	AssertArgumentType(list, CsoGateway.Collections.List, 'list in CsoGateway.Collections.List.prototype.AddRange')

	if(list === this)
		throw new Error('CsoGateway.Collections.List.AddRange: Cannot add the List to itself.');

	this.InsertArray(this.elements.length, list.elements);
}

/*
 * Appends all elements of an array to the end of the List.
 * The array parameter cannot be undefined or null. 
 * Elements in the array can be null but cannot be undefined.
 * The array parameter cannot be the underlying array of the List
 * or an exception is thrown.
 */
CsoGateway.Collections.List.prototype.AddArray = function(array)
{
	AssertArgumentType(array, Array, 'array in CsoGateway.Collections.List.prototype.AddArray')
	
	if(array === this.elements)
		throw new Error('CsoGateway.Collections.List.prototype.AddArray: Cannot add the List\'s underlying array to itself.');

	this.InsertArray(this.elements.length, array);
}

/*
 * Inserts an item to the List at the specified index.
 * The element cannot be undefined but can be null. If index is not specified, is undefined or null,
 * the element is appended to the end of this list. If the index is not of type Number,
 * is smaller than zero or larger than the current size of the List, an exception is thrown.
 * Otherwise, the element is inserted at the specified index and all elements
 * that occur after that index are shifted to the right (their indexes are
 * increased by 1).
 * Ref: System.Collection.IList.Insert
 */
CsoGateway.Collections.List.prototype.Insert = function(index, element)
{
	AssertArgumentIsNumberInRange(index, 0, this.elements.length, 'index in CsoGateway.Collections.List.prototype.Insert');
	AssertArgumentNotUndefined(element, 'element in CsoGateway.Collections.List.prototype.Insert');
	
	if(element === this)
		throw new Error('CsoGateway.Collections.List.prototype.AddRange: Cannot add the List to itself.');
			
	// shift elements to the right
	if(index < this.elements.length)
	{	
		for(var i = this.elements.length; i > index; i--)
			this.elements[i] = this.elements[i - 1];
	}

	this.elements[index] = element;
}

/*
 * Inserts all elements of the specified List in this List at a specified index.
 * The array parameter cannot be undefined or null. 
 * Elements in the List cannot be undefined but can be null.
 * If index is not specified, is undefined or null, the elements are appended
 * to the end of this list.
 * If the index is not of type Number, is smaller than zero or larger than
 * the current size of the List, an exception is thrown.
 * Otherwise, the element are inserted at the specified index in this List 
 * and all elements of the List that occur after that index are shifted to the
 * right.
 * Ref:  * Ref: System.Collection.Generic.List.InsertRange
 */
CsoGateway.Collections.List.prototype.InsertRange = function(index, list)
{
	AssertArgumentType(list, CsoGateway.Collections.List, 'list in CsoGateway.Collections.List.prototype.InsertRange')
	
	if(list === this)
		throw new Error('CsoGateway.Collections.List.prototype.InsertRange: Cannot add the List\'s own elements to the List.');
	
	
	this.InsertArray(index, list.elements);
}

/*
 * Inserts all elements of an array the List at a specified index.
 * The array parameter cannot be undefined or null. 
 * Elements in the array cannot be undefined but can be null.
 * If index is not specified, is undefined or null, the elements are appended
 * to the end of this list.
 * If the index is not of type Number, is smaller than zero or larger than
 * the current size of the List, an exception is thrown.
 * Otherwise, the element are inserted at the specified index in the List 
 * and all elements of the List that occur after that index are shifted to the right.
 */
CsoGateway.Collections.List.prototype.InsertArray = function(index, array)
{
	AssertArgumentIsNumberInRange(index, 0, this.elements.length, 'index in CsoGateway.Collections.List.prototype.InsertArray');
	AssertArgumentType(array, Array, 'array in CsoGateway.Collections.List.prototype.InsertArray')
	
	if(array === this.elements)
		throw new Error('CsoGateway.Collections.List.InsertArray: Cannot add the List\'s underlying array elements to the List.');

	for(var i = 0; i < array.length; ++i)
	{
		AssertArgumentNotUndefined(array[i], 'array[' + i + '] in CsoGateway.Collections.List.prototype.InsertArray');
	}
			
	// Shift elements to the right
	if(index < this.elements.length)
	{	
		for(var i = (this.elements.length + array.length - 1); i > (index + array.length - 1); i--)
			this.elements[i] = this.elements[i - array.length];
	}
	
	//Add element os the array in the List
	for(var i = index; i < index + array.length; ++i)
	{
		this.elements[i] = array[i - index];
	}
}

/*
 * Creates a shallow copy of a range of elements in the source List. Returns an Array
 * Ref: System.Collection.Generic.List.GetRange
 */
CsoGateway.Collections.List.prototype.GetRange = function(index, count)
{
	AssertArgumentIsNumberInRange(index, 0, (this.elements.length - 1), 'index in CsoGateway.Collections.List.prototype.GetRange');
	AssertArgumentIsNumberInRange(count, 0, (this.elements.length - index), 'count in CsoGateway.Collections.List.prototype.GetRange');
	
	var newRange = (this.elements.slice(index, (index+count)));
	var newList = new CsoGateway.Collections.List();
	for(var i=0; i<newRange.length; ++i)
		newList.Add(newRange[i]); 
	
	return newList;
}


/*
 * Removes all items from the List.
 * Ref: System.Collection.IList.Clear
 */
CsoGateway.Collections.List.prototype.Clear = function()
{	
	this.elements.splice(0, this.elements.length);
}

/*
 * Determines whether the List contains a specific item.
 * The element can be null but cannot be undefined or an exception is thrown.
 * Ref: System.Collection.IList.Contains
 */
CsoGateway.Collections.List.prototype.Contains = function(element)
{	
	return (this.IndexOf(element) != -1);
}



/*
 * Gets the first index of the specified item in the List, if it is present.
 * If not, this method returns -1.
 * The element can be null but cannot be undefined or an exception is thrown.
 * Ref: System.Collection.IList.IndexOf
 */
CsoGateway.Collections.List.prototype.IndexOf = function(element)
{
	AssertArgumentNotUndefined(element, 'element in CsoGateway.Collections.List.prototype.IndexOf');
	
	for(var i = 0; i < this.elements.length; i++)
		if(this.elements[i] === element)
			return i;
	return -1;
}

/*
 * Gets the last index of the specified element in the List, if it is present.
 * If not, this method returns -1.
 * The element can be null but cannot be undefined or an exception is thrown.
 */
CsoGateway.Collections.List.prototype.LastIndexOf = function(element)
{
	AssertArgumentNotUndefined(element, 'element in CsoGateway.Collections.List.prototype.LastIndexOf');
	
	for(var i = (this.elements.length - 1); i >= 0; i--)
		if(this.elements[i] === element)
			return i;
	return -1;
}

/*
 * Removes the first occurrence of a specific item from the List.
 * The element can be null but cannot be undefined or an exception is thrown.
 * If the element is successfully removed, all elements occuring after
 * the element removed are shifted left by 1).
 * This function return true is the item was found and removed and false otherwise.
 * Ref: System.Collection.IList.Remove
 */
CsoGateway.Collections.List.prototype.Remove = function(element)
{	
	AssertArgumentNotUndefined(element, 'element in CsoGateway.Collections.List.prototype.Remove');
	
	var firstIndexOfElement = this.IndexOf(element);
	
	var obj = null;
	if(firstIndexOfElement != -1)
	{
		obj = this.elements[firstIndexOfElement];
		this.elements.splice(firstIndexOfElement, 1);
	}
	
	return (obj != null);
}

/*
 * Removes and returns the element at the specified index.
 * If the index is not of type Number, is smaller than zero or larger than 
 * the current size of the List minus one, an exception is thrown.
 * If the element is successfully removed, all elements occuring after
 * the index of the removed element are shifted left by 1.
 * This function returns the object that was removed from the list unlike its .NET counterpart which returns void.
 * Ref: System.Collection.IList.RemoveAt
 */
CsoGateway.Collections.List.prototype.RemoveAt = function(index)
{	
	AssertArgumentIsNumberInRange(index, 0, (this.elements.length - 1), 'index in CsoGateway.Collections.List.prototype.RemoveAt');
	
	var obj = this.elements[index];
	
	this.elements.splice(index, 1);
	
	return obj;
}

/*
 * Creates a shallow copy of a range of elements in the source List.
 * Ref: System.Collection.Generic.List.RemoveRange
 */
CsoGateway.Collections.List.prototype.RemoveRange = function(index, count)
{	
	AssertArgumentIsNumberInRange(index, 0, (this.elements.length - 1), 'index in CsoGateway.Collections.List.prototype.RemoveRange');
	AssertArgumentIsNumberInRange(count, 0, (this.elements.length - index), 'count in CsoGateway.Collections.List.prototype.RemoveRange');
	
	return this.elements.splice(index, count);
}

/*
 * Sorts this list according to the specified sort function.
 * The sortFunc argument cannot be null or undefined or an exception is thrown.
 * Ref: System.Collection.Generic.List.Sort
 */
CsoGateway.Collections.List.prototype.Sort = function(sortFunc)
{
	if(sortFunc != null)
	{
		AssertArgumentType(sortFunc, Function, 'sortFunction in CsoGateway.Collections.List.prototype.Sort');
		this.elements.sort(sortFunc);
	}
	else
		this.elements.sort();
}

/*
 * Register the class CsoGateway.Collections.List
 */
CsoGateway.Collections.List.registerClass('CsoGateway.Collections.List', CsoNative, Sys.IDisposable);

/*
 * End of CsoGateway.Collections.List definition
 */